/*
 * Copyright 2011-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.sabayframework.mem.impl;

import org.sabayframework.mem.BigBuffer;


/**
 * BigBuffer is a version of {@link java.nio.Buffer} with long arguments.
 * @author Alex Antonau
 * */
public abstract class AbstractBigBuffer implements BigBuffer {
    // Invariants: position <= limit <= capacity
    protected long position = 0;
    private long limit;
    private long capacity;

    // Creates a new buffer with the given mark, position, limit, and capacity,
    // after checking invariants.
    //
    AbstractBigBuffer(long pos, long lim, long cap) {
		if (cap < 0)
		    throw new IllegalArgumentException();
		this.capacity = cap;
		limit(lim);
		position(pos);
    }
    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#capacity()
	 */
    @Override
	public final long capacity() {
    	return capacity;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#position()
	 */
    @Override
	public final long position() {
    	return position;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#position(long)
	 */
	@Override
	public final BigBuffer position(long newPosition) {
		if ((newPosition > limit) || (newPosition < 0))
			throw new IllegalArgumentException();
		position = newPosition;
		return this;
	}

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#limit()
	 */
    @Override
	public final long limit() {
    	return limit;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#limit(long)
	 */
	@Override
	public final BigBuffer limit(long newLimit) {
		if ((newLimit > capacity) || (newLimit < 0))
			throw new IllegalArgumentException();
		limit = newLimit;
		if (position > limit)
			position = limit;
		return this;
	}

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#clear()
	 */
    @Override
	public final BigBuffer clear() {
		position = 0;
		limit = capacity;
		return this;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#flip()
	 */
    @Override
	public final BigBuffer flip() {
		limit = position;
		position = 0;
		return this;
    }
    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#rewind()
	 */
    @Override
	public final BigBuffer rewind() {
		position = 0;
		return this;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#remaining()
	 */
    @Override
	public final long remaining() {
    	return limit - position;
    }

    /* (non-Javadoc)
	 * @see org.sabayframework.mem.impl.BigBuffer#hasRemaining()
	 */
    @Override
	public final boolean hasRemaining() {
    	return position < limit;
    }
    
	@Override
	public BigBuffer asReadOnlyBuffer() {
		return isReadOnly() ? this : new ReadOnlyBigBuffer(this);
	}
		
	/**
     * Returns the current hash code of this buffer.
     *
     * <p> The hash code of a buffer depends only upon its remaining
     * elements; that is, upon the elements from <tt>position()</tt> up to, and
     * including, the element at <tt>limit()</tt>&nbsp;-&nbsp;<tt>1</tt>.
     *
     * <p> Because buffer hash codes are content-dependent, it is inadvisable
     * to use buffers as keys in hash maps or similar data structures unless it
     * is known that their contents will not change.  </p>
     *
     * @return  The current hash code of this buffer
     */
    public int hashCode() {
		return hashCode(this);
    }

    /**
     * Returns the current hash code of given buffer.
     *
     * <p> The hash code of a buffer depends only upon its remaining
     * elements; that is, upon the elements from <tt>position()</tt> up to, and
     * including, the element at <tt>limit()</tt>&nbsp;-&nbsp;<tt>1</tt>.
     *
     * <p> Because buffer hash codes are content-dependent, it is inadvisable
     * to use buffers as keys in hash maps or similar data structures unless it
     * is known that their contents will not change.  </p>
     *
     * @return  The current hash code of this buffer
     */
    public static int hashCode(final BigBuffer b){
		int h = 1;
		long p = b.position();
		for (long i = b.limit() - 1; i >= p; i--)
		    h = 31 * h + (int)b.get(i);
		return h;
    }
    /**
     * Tells whether or not this buffer is equal to another object.
     *
     * <p> Two big buffers are equal if, and only if,
     *
     * <p><ol>
     *
     *   <li><p> They have the same number of remaining elements, and
     *   </p></li>
     *
     *   <li><p> The two sequences of remaining elements, considered
     *   independently of their starting positions, are pointwise equal.
     *   </p></li>
     *
     * </ol>
     *
     * <p> A big buffer is not equal to any other type of object.  </p>
     *
     * @param  ob  The object to which this buffer is to be compared
     *
     * @return  <tt>true</tt> if, and only if, this buffer is equal to the
     *           given object
     */
    public boolean equals(Object ob) {
		if (!(ob instanceof BigBuffer)) return false;
		BigBuffer that = (BigBuffer)ob;
		if (this.remaining() != that.remaining()) return false;
		long p = this.position();
		for (long i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--) {
		    byte v1 = this.get(i);
		    byte v2 = that.get(j);
		    if (v1 != v2) return false;
		}
		return true;
    }

    public static int compare(final BigBuffer b1,final BigBuffer b2){
		long n = b1.position() + Math.min(b1.remaining(), b2.remaining());
		for (long i = b1.position(), j = b2.position(); i < n; i++, j++) {
		    byte v1 = b1.get(i);
		    byte v2 = b2.get(j);
		    if (v1 == v2) continue;
		    if (v1 < v2) return -1;
		    return +1;
		}
		final long diff = b1.remaining() - b2.remaining();
		return diff < 0 ? -1 : diff == 0 ? 0 : +1;
    }
    /**
     * Compares this buffer to another.
     *
     * <p> Two big buffers are compared by comparing their sequences of
     * remaining elements lexicographically, without regard to the starting
     * position of each sequence within its corresponding buffer.
     *
     * <p> A big buffer is not comparable to any other type of object.
     *
     * @return  A negative integer, zero, or a positive integer as this buffer
     *		is less than, equal to, or greater than the given buffer
     */
    public int compareTo(BigBuffer that) {
    	return compare(this, that);
    }
    /**
     * Returns a string summarizing the state of this buffer.  </p>
     *
     * @return  A summary string
     */
    public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(getClass().getName());
		sb.append("[pos=");
		sb.append(position());
		sb.append(" lim=");
		sb.append(limit());
		sb.append(" cap=");
		sb.append(capacity());
		sb.append("]");
		return sb.toString();
    }
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		destroy();
	}

}
